﻿using DotNetCommon.Extensions;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace DotNetCommon
{
    internal class CacheFileStreamContext
    {
        public long LastId { get; set; }
        public string LastDateText { get; set; }
    }

    /// <summary>
    /// 分布式生成流水号时使用的缓存器,因为docker部署时可能会清空目录，所以需要将生成的流水号缓存到其他地方<para></para>
    /// 非docker环境下忽略
    /// </summary>
    public interface IDistributeGeneratorCache
    {
        /// <summary>
        /// 初始读取所有缓存项
        /// </summary>
        /// <returns></returns>
        ConcurrentDictionary<string, ConcurrentDictionary<string, long>> InitReadCache();

        /// <summary>
        /// 写入缓存项
        /// </summary>
        /// <param name="staticText"></param>
        /// <param name="dateText"></param>
        /// <param name="id"></param>
        void WriteCache(string staticText, string dateText, long id);
    }

    /// <summary>
    /// 使用本地文件缓存流水号生成数据
    /// </summary>
    public class DistributeGeneratorFileCache : IDistributeGeneratorCache
    {
        /// <summary>
        /// 缓存及目录
        /// </summary>
        public string CacheDir { get; set; } = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ".cache");

        //unc路径
        //public static string CacheDir = @"\\Jackletter-PC\分布式流水号生成缓存\.cache";

        private ConcurrentDictionary<string, object> _locks = new ConcurrentDictionary<string, object>();

        /// <summary>
        /// 读取并压缩缓存文件
        /// </summary>
        /// <param name="filePath"></param>
        /// <returns></returns>
        private ConcurrentDictionary<string, long> _initFileCache(string filePath)
        {
            var lines = File.ReadAllLines(filePath).ToList();
            //初始化snoCaches
            var idcaches = new ConcurrentDictionary<string, long>();

            if (lines.Count > 0)
            {
                for (var j = lines.Count - 1; j >= 0; j--)
                {
                    var (dateText, id) = parseLine(lines[j]);
                    if (idcaches.TryGetValue(dateText, out long _id))
                    {
                        if (_id < id) idcaches.TryUpdate(dateText, id, _id);
                    }
                    else
                    {
                        idcaches.TryAdd(dateText, id);
                    }
                }
            }
            //复写缓存文件
            lines = idcaches.Select(i => $"{i.Key}={i.Value}").ToList();
            File.Delete(filePath);
            File.AppendAllLines(filePath, lines);
            return idcaches;
        }

        /// <summary>
        /// 初始读取所有缓存项
        /// </summary>
        /// <returns></returns>
        public ConcurrentDictionary<string, ConcurrentDictionary<string, long>> InitReadCache()
        {
            var dir = Path.Combine(CacheDir);
            Directory.CreateDirectory(dir);
            //读取各个缓存中的文件到内存
            var snoCaches = new ConcurrentDictionary<string, ConcurrentDictionary<string, long>>();
            var files = new DirectoryInfo(dir).GetFiles("*.log");
            for (var i = 0; i < files.Length; i++)
            {
                var filepath = files[i].FullName;
                var staticText = Path.GetFileNameWithoutExtension(filepath);

                var idcaches = _initFileCache(filepath);
                snoCaches.GetOrAdd(staticText, idcaches);
            }
            return snoCaches;
        }

        private static (string dateText, long id) parseLine(string line)
        {
            var index = line.LastIndexOf('=');
            var dateText = line.Substring(0, index);
            var idstr = line.Substring(index + 1);
            var id = long.Parse(idstr);
            return (dateText, id);
        }


        private ConcurrentDictionary<string, int> _counters = new ConcurrentDictionary<string, int>();
        /// <summary>
        /// 写入缓存项
        /// </summary>
        /// <param name="staticText"></param>
        /// <param name="dateText"></param>
        /// <param name="id"></param>
        public void WriteCache(string staticText, string dateText, long id)
        {
            Directory.CreateDirectory(CacheDir);

            var lockObj = _locks.GetOrAdd(staticText, new object());
            lock (lockObj)
            {
                var filePath = Path.Combine(CacheDir, staticText + ".log");
                var line = $"{dateText}={id}{Environment.NewLine}";
                File.AppendAllText(filePath, line);
                if (_counters.TryGetValue(staticText, out int counter))
                {
                    var newCounter = counter + 1;
                    if (newCounter > 1000)
                    {
                        _initFileCache(filePath);
                        //压缩文件行数
                        _initFileCache(filePath);
                        newCounter = 0;
                    }
                    _counters.TryUpdate(staticText, newCounter, counter);
                }
                else
                {
                    _counters.TryAdd(staticText, 1);
                }
            }
            return;
        }
    }

    /// <summary>
    /// 基于分布式的 <c>Id</c> 和 <c>流水号</c> 生成控制器 (Id生成使用雪花算法 <seealso cref="SnowflakeIdWorker"/>)
    /// </summary>
    /// <remarks>
    /// 使用时需要先设置当前计算机节点的 <c>MachineId</c> 和 <c>MachineIdString</c> ，参照： <seealso cref="Machine.SetMachineId(int,string)"/>
    /// </remarks>
    public class DistributeGenerator
    {
        private DistributeGenerator() { }

        private const string defaultKey = "__inner_distribute_key";

        #region Id生成控制
        private static ConcurrentDictionary<string, SnowflakeIdWorker> ht_workers = new ConcurrentDictionary<string, SnowflakeIdWorker>();

        /// <summary>
        /// 根据指定的Key值生成Id (同key值的Id生成有先后顺序,可以将数据库名+表名联合起来作为key值)
        /// </summary>
        /// <param name="key">id类型关键字(可以组合数据库+表名+列名)</param>
        /// <remarks>
        /// 这里使用的雪花算法参数为: 
        /// <list type="bullet">
        /// <item>时间精度: 毫秒<para></para></item>
        /// <item>时间位数: 41,可使用69年<para></para></item>
        /// <item>机器位数: 10,可表示1024台机器<para></para></item>
        /// <item>序列号位数: 12，可表示4096/ms，即：409万QPS</item>
        /// </list>
        /// 如果想使用其他的雪花参数，请直接使用 <seealso cref="SnowflakeIdWorker.NextId()"/>
        /// </remarks>
        /// <returns></returns>
        public static long NewId(string key = null)
        {
            if (string.IsNullOrWhiteSpace(key)) key = defaultKey;
            var work = ht_workers.GetOrAdd(key, _ => new SnowflakeIdWorker(Machine.MachineId));
            return work.NextId();
        }
        #endregion

        #region 流水号生成控制
        private static ConcurrentDictionary<string, ConcurrentDictionary<string, long>> snoCaches = null;
        /// <summary>
        /// 默认使用文件缓存,可以使用自定义redis缓存
        /// </summary>
        public static IDistributeGeneratorCache DistributeGeneratorCache = new DistributeGeneratorFileCache();

        /// <summary>
        /// 生成流水号，使用两层并发字典做缓存控制序列号自增（第一层采key + StaticText + MachineText组合作为key，第二层采用DateText作为key）。<para></para>
        /// 注意：key值是用来区分不同情况下流水号生成的，比如有两个类似的数据库test1和test2，为它们的表t_person分别生成相同前缀的流水号：<para></para>
        /// <code>DistributeGenerator.NewSNO("test1",SerialFormat.CreateDistributeFast("SNO"));</code>
        /// <code>DistributeGenerator.NewSNO("test2",SerialFormat.CreateDistributeFast("SNO"));</code>
        /// 注意：为了保证程序重启后的流水号连续，程序默认使用 "AppDomain.CurrentDomain.BaseDirectory/.cache" 目录缓存数据，当在docker中运行时需要将此目录映射到容器外部以防止数据丢失，如：<para></para>
        /// <code>docker run -e "ASPMETCORE_ENVIRONEMENT=Production" -e "MachineId=1" -e "MachineIdString=TEST" -v /etc/localtime:/etc/localtime -v /test/webtest/.cache:/app/.cache -d -p 6565:5000 --name=webtest dockerwebtest:testtag</code>
        /// 更多缓存方式，参考: <seealso cref="IDistributeGeneratorCache"/>
        /// 
        /// </summary>
        /// <param name="format">流水号格式</param>
        /// <param name="key">用于区分不同流水号生成，比如：<para></para>
        /// 操作两个类似数据库test1和test2，对于它们各自的表t_person分别生成相同前缀的流水号</param>
        /// <remarks>
        /// 流水号格式：
        /// <list type="bullet">
        /// <item>有且只有一个序列号块(SerialNo),并且在末尾；</item>
        /// <item>至少有一个时间格式块(DateText)；</item>
        /// </list>
        /// </remarks>
        /// <returns></returns>
        public static string NewSNO(string key, SerialFormat format)
        {
            ensureInitalCache();
            if (format == null) throw new ArgumentNullException(nameof(format));
            SerialFormat.ValidFormat(format);
            if (key.IsNullOrEmptyOrWhiteSpace()) key = defaultKey;
            //使用两层并发字典
            //第一层: key是静态文本
            //第二层: key是时间戳
            DateTime now = DateTime.Now;
            var (staticText, dateText, needCache) = parseChunkFormat(format.Chunks, now);
            staticText = key + "+" + staticText;
            //创建或获取第一层字典
            var caches = snoCaches.GetOrAdd(staticText, new ConcurrentDictionary<string, long>());
            //创建或更新第二层字典
            var id = caches.AddOrUpdate(dateText, 1, (key, old) => old + 1);
            //组装流水号
            var chunks = format.Chunks;
            var sno = "";
            for (int i = 0, len = chunks.Count; i < len; i++)
            {
                var chunk = chunks[i];
                if (chunk.Type == SerialFormatChunkType.StaticText)
                {
                    sno += chunk.FormatString;
                }
                else if (chunk.Type == SerialFormatChunkType.DateText)
                {
                    sno += now.ToString(chunk.FormatString);
                }
                else if (chunk.Type == SerialFormatChunkType.MachineText)
                {
                    sno += Machine.MachineIdString.PadLeft(4, '0');
                }
                else if (chunk.Type == SerialFormatChunkType.SerialNo)
                {
                    var s = id.ToString();
                    if (s.Length > chunk.Length)
                    {
                        sno += s;
                    }
                    else
                    {
                        sno += s.PadLeft(chunk.Length, '0');
                    }
                }
            }
            //将生成记录写入到缓存
            if (needCache)
            {
                DistributeGeneratorCache.WriteCache(staticText, dateText, id);
            }
            return sno;
        }

        private static bool isInitCache = false;
        private static void ensureInitalCache()
        {
            if (isInitCache) return;
            lock (typeof(DistributeGenerator))
            {
                if (isInitCache) return;
                snoCaches = DistributeGeneratorCache.InitReadCache();
                isInitCache = true;
            }
        }

        /// <summary>
        /// 解析流水号格式定义,返回静态文本和日期时间戳<para></para>
        /// <list type="bullet">
        /// <item>staticText：静态文本，组合机器码和静态文本 </item>
        /// <item>dateText：日期时间戳 </item>
        /// <item>needCache：是否需要文件缓存，至少分钟内序列递增的才需要文件缓存 </item>
        /// </list>
        /// </summary>
        /// <param name="chunks"></param>
        /// <param name="now"></param>
        /// <returns></returns>
        private static (string staticText, string dateText, bool needCache) parseChunkFormat(List<SerialFormatChunk> chunks, DateTime now)
        {
            var staticText = "";
            var dateText = "";
            SerialFormatChunk chunk = null;
            bool needCache = true;
            DateTimePrecision pre = DateTimePrecision.Year;
            for (int i = 0, len = chunks.Count; i < len; i++)
            {
                chunk = chunks[i];
                if (chunk.Type == SerialFormatChunkType.StaticText)
                {
                    staticText += chunk.FormatString;
                }
                else if (chunk.Type == SerialFormatChunkType.DateText)
                {
                    dateText += now.ToString(chunk.FormatString);
                    var tmp = parsePrecision(chunk.FormatString);
                    if (tmp > pre) pre = tmp;
                }
                else if (chunk.Type == SerialFormatChunkType.MachineText)
                {
                    staticText += Machine.MachineIdString.PadLeft(4, '0');
                }
            }
            if (pre >= DateTimePrecision.Second) needCache = false;
            return (staticText, dateText, needCache);
        }

        private static ConcurrentDictionary<string, DateTimePrecision> _dicPrecisions = new ConcurrentDictionary<string, DateTimePrecision>();
        /// <summary>
        /// 判断DateTime格式字符串的精度
        /// </summary>
        /// <param name="formatstr"></param>
        /// <returns></returns>
        private static DateTimePrecision parsePrecision(string formatstr)
        {
            if (_dicPrecisions.ContainsKey(formatstr)) return _dicPrecisions[formatstr];
            DateTimePrecision res = DateTimePrecision.Year;
            var dt = new DateTime(2020, 02, 03, 04, 05, 06, 789);
            var str2 = dt.ToString(formatstr);
            var dt2 = DateTime.ParseExact(str2, formatstr, System.Globalization.CultureInfo.CurrentCulture);
            if (dt2.Millisecond == 789)
            {
                res = DateTimePrecision.Millisecond;
            }
            if (dt2.Second == 6)
            {
                res = DateTimePrecision.Second;
            }
            if (dt2.Minute == 5)
            {
                res = DateTimePrecision.Minute;
            }
            if (dt2.Hour == 4)
            {
                res = DateTimePrecision.Hour;
            }
            if (dt2.Day == 3)
            {
                res = DateTimePrecision.Day;
            }
            if (dt2.Month == 2)
            {
                res = DateTimePrecision.Month;
            }
            if (dt2.Year == 2020)
            {
                res = DateTimePrecision.Year;
            }
            return _dicPrecisions.GetOrAdd(formatstr, res);
        }

        #endregion
    }

    /// <summary>
    /// DateTime.ToString()的格式字符串精度级别
    /// </summary>
    internal enum DateTimePrecision
    {
        Year,
        Month,
        Day,
        Hour,
        Minute,
        Second,
        Millisecond
    }
    #region 流水号格式

    /// <summary>
    /// 流水块定义格式
    /// </summary>
    public class SerialFormatChunk
    {
        /// <summary>
        /// 流水块类型
        /// </summary>
        public SerialFormatChunkType Type { set; get; }
        /// <summary>
        /// 流水块格式
        /// </summary>
        public string FormatString { set; get; }
        /// <summary>
        /// 流水块长度
        /// </summary>
        public int Length { set; get; }
    }

    /// <summary>
    /// 流水块类型
    /// </summary>
    public enum SerialFormatChunkType
    {
        /// <summary>
        /// 静态文本
        /// </summary>
        StaticText,
        /// <summary>
        /// 日期
        /// </summary>
        DateText,
        /// <summary>
        /// 机器码
        /// </summary>
        MachineText,
        /// <summary>
        /// 序列号
        /// </summary>
        SerialNo
    }

    /// <summary>
    ///流水号格式<para></para>
    ///<list type="bullet">
    ///<item>有且只有一个序列号块(SerialNo),并且在末尾；</item>
    ///<item>至少有一个时间格式块(DateText)；</item>
    /// </list>
    /// </summary>
    public class SerialFormat
    {
        private SerialFormat() { }
        /// <summary>
        /// 创建流水号格式
        /// </summary>
        /// <param name="prefix">流水号前缀</param>
        /// <param name="dateFormat">日期格式串</param>
        /// <param name="minSerialLen">流水号末尾序列块的最小长度；参考：<seealso cref="SerialFormat"/></param>
        /// <param name="hasMachine">是否启用机器码(默认不启用,分布式环境下务必启用!)</param>
        /// <returns></returns>
        public static SerialFormat CreateFast(string prefix, string dateFormat = "yyyyMMdd", int minSerialLen = 6, bool hasMachine = false)
        {
            var format = new SerialFormat()
            {
                Chunks = new List<SerialFormatChunk>()
                {
                    new SerialFormatChunk()
                    {
                        Type=SerialFormatChunkType.StaticText,
                        FormatString=prefix
                    },
                    new SerialFormatChunk()
                    {
                        Type=SerialFormatChunkType.DateText,
                        FormatString=dateFormat
                    }
                }
            };
            if (hasMachine)
            {
                format.Chunks.Add(new SerialFormatChunk()
                {
                    Type = SerialFormatChunkType.MachineText
                });
            }
            format.Chunks.Add(new SerialFormatChunk()
            {
                Type = SerialFormatChunkType.SerialNo,
                Length = minSerialLen
            });
            return format;
        }

        /// <summary>
        /// 创建流水号格式,启用分布式生成
        /// </summary>
        /// <param name="prefix">流水号前缀</param>
        /// <param name="dateFormat">日期格式串</param>
        /// <param name="minSerialLen">流水号末尾序列块的最小长度；参考：<seealso cref="SerialFormat"/></param>
        /// <returns></returns>
        public static SerialFormat CreateDistributeFast(string prefix, string dateFormat = "yyyyMMdd", int minSerialLen = 6) => CreateFast(prefix, dateFormat, minSerialLen, true);

        /// <summary>
        /// 创建流水号格式
        /// </summary>
        /// <param name="chunks"></param>
        /// <returns></returns>
        public static SerialFormat CreateByChunks(List<SerialFormatChunk> chunks)
        {
            var format = new SerialFormat() { Chunks = chunks };
            ValidFormat(format);
            return format;
        }

        /// <summary>
        /// 解析指定的流水号规则
        /// </summary>
        /// <param name="format">流水号格式</param>
        /// <param name="now">当前日期</param>
        /// <param name="machineIdString">机器Id</param>
        /// <returns>(string likeStr, DateTime snoNow, int startIndex)</returns>
        public static (string likeStr, DateTime snoNow, int startIndex) Parse(SerialFormat format, DateTime? now, string machineIdString)
        {
            if (now == null) now = DateTime.Now;
            ValidFormat(format);
            int startindex = 0;
            DateTime snoNow = DateTime.MinValue;
            var likestr = "";
            var chunks = format.Chunks;
            for (int i = 0, len = chunks.Count; i < len; i++)
            {
                var chunk = chunks[i];
                if (chunk.Type == SerialFormatChunkType.StaticText)
                {
                    likestr += chunk.FormatString;
                }
                else if (chunk.Type == SerialFormatChunkType.DateText)
                {
                    var nowstr = now.Value.ToString(chunk.FormatString);
                    likestr += nowstr;
                    snoNow = DateTime.ParseExact(nowstr, format.Chunks.FirstOrDefault(k => k.Type == SerialFormatChunkType.DateText).FormatString, System.Globalization.CultureInfo.CurrentCulture);
                }
                else if (chunk.Type == SerialFormatChunkType.MachineText)
                {
                    likestr += machineIdString.PadLeft(4, '0');
                }
                else if (chunk.Type == SerialFormatChunkType.SerialNo)
                {
                    startindex = likestr.Length;
                    likestr += "%";
                }
            }
            return (likestr, snoNow, startindex);
        }

        /// <summary>
        /// 流水号定义格式块集合
        /// </summary>
        public List<SerialFormatChunk> Chunks { private set; get; }

        /// <summary>
        /// 检测流水号是否符合格式规则
        /// <list type="bullet">
        /// <item>有且只有一个序列号块(SerialNo),并且在末尾；</item>
        /// <item>至少有一个时间格式块(DateText)；</item>
        /// </list>
        /// </summary>
        /// <param name="format">流水号格式</param>
        /// <returns></returns>
        public static void ValidFormat(SerialFormat format)
        {
            if (format == null) throw new Exception("流水号格式为空!");
            if (format.Chunks == null && format.Chunks.Count == 0) throw new Exception("流水号格式块中至少存在一个日期格式块和序列号块!");
            var chunks = format.Chunks;
            if (chunks.FirstOrDefault(i => i.Type == SerialFormatChunkType.DateText) == null) throw new Exception("流水号格式中至少有一个日期格式块!");
            if (chunks[chunks.Count - 1].Type != SerialFormatChunkType.SerialNo) throw new Exception("流水号格式块的最后一个必须是序列号快!");
        }
    }
    #endregion

    #region 雪花算法
    /// <summary>
    /// 扩展雪花Id生成算法<para></para>
    /// Twitter_Snowflake（标准雪花Id）: <c>long</c> 型, 64bit位整数，分成：1 + 41 + 10 + 12 四部分<para></para>
    /// <term>1bit</term>固定为1，表示正整数<para></para>
    /// <term>41bit</term>记录从2020-01-01到现在的毫秒数，最多可表示69年<para></para>
    /// <term>10bit</term>机器Id，最多可表示1024台机器<para></para>
    /// <term>12bit</term>序列号，最大并发，4096/ms，即：409万QPS<para></para>
    /// 扩展后：<para></para>
    /// <list type="bullet">
    /// <item>可自定义时间精度，秒或毫秒；</item>
    /// <item>可自定义时间戳位数；</item>
    /// <item>可自定义机器Id位数；</item>
    /// <item>可自定义序列号位数；</item>
    /// <item>可根据Id反向解析时间_机器Id_序列号；</item>
    /// </list>
    /// </summary>
    public class SnowflakeIdWorker
    {
        /// <summary>
        /// 雪花算法的时间精度
        /// </summary>
        public enum WorkerTimeModel
        {
            /// <summary>
            /// 秒级
            /// </summary>
            Second,
            /// <summary>
            /// 毫秒级
            /// </summary>
            Millisecond
        }

        /// <summary>
        /// 开始时间戳，默认：2020-01-01 (UTC)
        /// </summary>
        public DateTime StartTime { get; }

        /// <summary>
        /// 时间精度，默认: 毫秒级
        /// </summary>
        public WorkerTimeModel TimeModel { get; }

        /// <summary>
        /// 时间戳占用的bit位数
        /// </summary>
        public int TimeLength { get; }

        /// <summary>
        /// 机器Id占用的bit位数
        /// </summary>
        public int MachineLength { get; }

        /// <summary>
        /// 序列号占用的bit位数
        /// </summary>
        public int SequenceLength { get; }

        /// <summary>
        /// 机器Id，>=0， 默认: 0
        /// </summary>
        public long MachineId { get; }

        /// <summary>
        /// 构建雪花Id生成器
        /// </summary>
        /// <param name="machineId">机器Id, >=0 </param>
        /// <param name="startTime">起始时间, >=2020年, 默认：2020-01-01 </param>
        /// <param name="timeModel">时间精度，默认：毫秒</param>
        /// <param name="timeLength">时间戳bit位数：[28,44]，默认：41，即: 69年</param>
        /// <param name="machineLength">机器Id的bit位数：[5,18]，默认：10，即：1024台</param>
        /// <param name="sequenceLength">序列号的bit位数：[2,24]，默认：12，即：408万QPS</param>
        public SnowflakeIdWorker(
            int machineId,
            DateTime? startTime = null,
            WorkerTimeModel timeModel = WorkerTimeModel.Millisecond,
            int timeLength = 41,
            int machineLength = 10,
            int sequenceLength = 12)
        {
            //设置machineId
            if (machineId < 0) throw new Exception("机器Id必须大于等于0!");
            MachineId = machineId;
            //设置startTime
            if (startTime == null) StartTime = new DateTime(2020, 1, 1, 0, 0, 0, DateTimeKind.Utc);
            else
            {
                if (startTime < DateTime.Parse("2020-01-01")) throw new Exception("时间戳的起始时间不能小于 \"2020-01-01\"!");
                StartTime = startTime.Value.ToUniversalTime();
            }
            //设置timeModel
            TimeModel = timeModel;
            //设置timeLength
            if (timeLength < 28 || timeLength > 44) throw new Exception("时间戳bit位数必须在28-44范围内!");
            TimeLength = timeLength;
            //设置machineLength
            if (machineLength < 5 || machineLength > 18) throw new Exception("机器Id的bit位数必须在5-18范围内!");
            MachineLength = machineLength;
            //设置sequenceLength
            if (sequenceLength < 2 || sequenceLength > 24) throw new Exception("序列号的bit位数必须在2-24范围内!");
            SequenceLength = sequenceLength;

            //最后检查machineId和machineLength是否匹配
            if (Math.Pow(2, machineLength) <= machineId) throw new Exception($"当前设置的机器Id的bit位数:{machineLength} 不能表示机器Id的值: {machineId} !");

            //序列号掩码
            sequenceMask = -1L ^ (-1L << sequenceLength);
        }



        private long lastTimestamp = 0;
        private long sequence = 0;

        private long sequenceMask = 0;

        /// <summary>
        /// 获得下一个ID
        /// </summary>
        /// <returns></returns>
        public long NextId()
        {
            lock (this)
            {
                //获取当前时间戳
                long timestamp = GetCurrentTimestamp();
                if (timestamp != lastTimestamp)
                {
                    //时间戳改变，序列重置,不管是否回拨，总数依赖当前时间戳生成Id
                    sequence = 1L;
                }
                else if (timestamp == lastTimestamp)
                {
                    //时间戳相同, 序列自增
                    sequence = (sequence + 1) & sequenceMask;
                    if (sequence == 0)
                    {
                        //序列溢出，阻塞获得新的时间戳
                        timestamp = GetNextTimestamp(lastTimestamp);
                        sequence = 1L;
                    }
                }

                //移位并通过或运算拼到一起组成64位的ID
                var id = (timestamp << (SequenceLength + MachineLength))
                        | (MachineId << SequenceLength)
                        | sequence;

                //重置时间戳
                lastTimestamp = timestamp;

                return id;
            }
        }

        /// <summary>
        /// 解析雪花ID
        /// </summary>
        /// <returns></returns>
        public string AnalyzeId(long Id)
        {
            StringBuilder sb = new StringBuilder();

            var timestamp = (Id >> SequenceLength + MachineLength);
            var time = StartTime + (TimeModel == WorkerTimeModel.Second ? TimeSpan.FromSeconds(timestamp) : TimeSpan.FromMilliseconds(timestamp));
            sb.Append(time.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss.fffzzz"));

            var machineId = (Id ^ (timestamp << (SequenceLength + MachineLength))) >> SequenceLength;
            sb.Append("_" + machineId);

            var sequence = Id & sequenceMask;
            sb.Append("_" + sequence);

            return sb.ToString();
        }

        /// <summary>
        /// 获取当前时间戳
        /// </summary>
        /// <returns></returns>
        private long GetCurrentTimestamp()
        {
            var span = DateTime.UtcNow - StartTime;
            return TimeModel == WorkerTimeModel.Millisecond ? (long)span.TotalMilliseconds : (long)span.TotalSeconds;
        }

        /// <summary>
        /// 阻塞直到获得新的时间戳
        /// </summary>
        /// <param name="lastTimestamp">上次生成ID的时间戳</param>
        /// <returns>当前时间戳</returns>
        private long GetNextTimestamp(long lastTimestamp)
        {
            long timestamp = GetCurrentTimestamp();
            while (timestamp <= lastTimestamp)
            {
                if (TimeModel == WorkerTimeModel.Second)
                {
                    Thread.Sleep(200);
                }
                timestamp = GetCurrentTimestamp();
            }
            return timestamp;
        }
    }
    #endregion
}
